// @Author EthanScriptOn
// @Desc
package resource

import (
	"errors"
	"fmt"
	"gitee.com/fatzeng/srf_switch_basic_components/proxy/interceptor"
	"gitee.com/fatzeng/srf_switch_basic_components/tool"
	"math"
	"sort"
)

type ResourceLevel string

// ResourceManagerUpGradationMultiLevel multi level explorer
type ResourceManagerUpGradationMultiLevel struct {
	resourceManager *ResourceManager
	accelerateSeek  map[interface{}][]ResourceLevel
	byLevel         map[ResourceLevel]map[interface{}]struct{}
}

func GenerateResourceManagerUpGradationMultiLevel() *ResourceManagerUpGradationMultiLevel {
	return &ResourceManagerUpGradationMultiLevel{
		resourceManager: GenerateResourceManager(),
		byLevel:         make(map[ResourceLevel]map[interface{}]struct{}),
		accelerateSeek:  make(map[interface{}][]ResourceLevel),
	}
}

// AddResource add resources
func (d *ResourceManagerUpGradationMultiLevel) AddResource(resourceEntity ResourceEntity, resourceLevels []ResourceLevel, cover bool) (err error) {
	defer tool.HandleErr(&err)
	if resourceEntity == nil {
		return errors.New("resourceEntity is empty")
	}
	if len(resourceLevels) <= 0 {
		return errors.New("resourceLevels is empty")
	}
	if err = resourceEntity.IsValid(); err != nil {
		return err
	}
	identifier, err := resourceEntity.GenerateUniqueIdentifier()
	if err != nil {
		return err
	}
	options := &ResourceOptions{
		PreOption:   make([]*interceptor.InterceptorOptions, 0),
		AfterOption: make([]*interceptor.InterceptorOptions, 0),
		DoExecute: func(container map[interface{}]interface{}) (err error) {
			container[identifier] = resourceEntity
			return
		},
		Interrupt: false,
	}
	// If you add a pre-operation, the resource cannot already exist
	if preInterceptorOptions, err := interceptor.GenerateInterceptorOptions(func() (err error) {
		for _, level := range resourceLevels {
			identifiersMap, has := d.byLevel[level]
			if !has {
				options.Interrupt = false
				return
			}
			if _, ok := identifiersMap[identifier]; ok && !cover {
				return fmt.Errorf("resource with id: [%d] already exists in level: [%+v]", identifier, level)
			}
		}
		return
	}); err != nil {
		return err
	} else {
		options.PreOption = append(options.PreOption, preInterceptorOptions)
	}
	// The post-operation is added to maintain the index information
	if afterInterceptorOptions, err := interceptor.GenerateInterceptorOptions(func() (err error) {
		for _, level := range resourceLevels {
			levelData, ok := d.byLevel[level]
			if !ok {
				levelData = make(map[interface{}]struct{})
				d.byLevel[level] = levelData
			}
			levelData[identifier] = struct{}{}
		}
		// index information for quick finding
		d.accelerateSeek[identifier] = resourceLevels
		return
	}); err != nil {
		return err
	} else {
		options.AfterOption = append(options.AfterOption, afterInterceptorOptions)
	}
	return d.resourceManager.OperateResource(options)
}

// DelResourceWithLevels delete resources based on level
func (d *ResourceManagerUpGradationMultiLevel) DelResourceWithLevels(resourceLevels []ResourceLevel) (err error) {
	defer tool.HandleErr(&err)
	if len(resourceLevels) <= 0 {
		return errors.New("resourceLevels is empty")
	}
	delIdentifiers := make([]interface{}, 0)
	options := &ResourceOptions{
		PreOption:   make([]*interceptor.InterceptorOptions, 0),
		AfterOption: make([]*interceptor.InterceptorOptions, 0),
		DoExecute: func(container map[interface{}]interface{}) (err error) {
			if len(delIdentifiers) <= 0 {
				return
			}
			for _, identifier := range delIdentifiers {
				delete(container, identifier)
			}
			return
		},
		Interrupt: false,
	}
	if preInterceptorOptions, err := interceptor.GenerateInterceptorOptions(func() (err error) {
		for _, level := range resourceLevels {
			identifierMap, ok := d.byLevel[level]
			if !ok {
				continue
			}
			// find the item you want to delete
			for identifier := range identifierMap {
				delIdentifiers = append(delIdentifiers, identifier)
			}
		}
		// If it cannot be found, it does not need to be deleted, and the execution is interrupted
		if len(delIdentifiers) <= 0 {
			options.Interrupt = true
			return
		}
		return
	}); err != nil {
		return err
	} else {
		options.PreOption = append(options.PreOption, preInterceptorOptions)
	}
	// maintain index information
	if afterInterceptorOptions, err := interceptor.GenerateInterceptorOptions(func() (err error) {
		if len(delIdentifiers) <= 0 {
			return
		}
		for _, identifier := range delIdentifiers {
			delete(d.accelerateSeek, identifier)
		}
		for _, level := range resourceLevels {
			delete(d.byLevel, level)
		}
		return
	}); err != nil {
		return err
	} else {
		options.AfterOption = append(options.AfterOption, afterInterceptorOptions)
	}
	return d.resourceManager.OperateResource(options)
}

func (d *ResourceManagerUpGradationMultiLevel) DelResourceWithId(identifier interface{}) (err error) {
	defer tool.HandleErr(&err)
	if identifier == nil {
		return errors.New("identifier is empty")
	}
	options := &ResourceOptions{
		PreOption:   make([]*interceptor.InterceptorOptions, 0),
		AfterOption: make([]*interceptor.InterceptorOptions, 0),
		DoExecute: func(container map[interface{}]interface{}) (err error) {
			delete(container, identifier)
			return
		},
		Interrupt: false,
	}
	delLevels := make([]ResourceLevel, 0)
	// Find the item you want to delete based on the level
	if preInterceptorOptions, err := interceptor.GenerateInterceptorOptions(func() (err error) {
		levels, ok := d.accelerateSeek[identifier]
		if !ok {
			options.Interrupt = true
			return
		}
		delLevels = levels
		return
	}); err != nil {
		return err
	} else {
		options.PreOption = append(options.PreOption, preInterceptorOptions)
	}
	// maintain index information after deletion
	if afterInterceptorOptions, err := interceptor.GenerateInterceptorOptions(func() (err error) {
		if len(delLevels) <= 0 {
			return fmt.Errorf("not found level for resource with id: %d", identifier)
		}
		for _, level := range delLevels {
			delete(d.byLevel[level], identifier)
		}
		delete(d.accelerateSeek, identifier)
		return
	}); err != nil {
		return err
	} else {
		options.AfterOption = append(options.AfterOption, afterInterceptorOptions)
	}
	return d.resourceManager.OperateResource(options)
}

// FoundResourceWithId find resources based on id
func (d *ResourceManagerUpGradationMultiLevel) FoundResourceWithId(identifier interface{}) (ResourceEntity, error) {
	resource, err, hasResource := d.resourceManager.GetResource(identifier)
	if err != nil {
		return nil, err
	}
	if !hasResource {
		return nil, nil
	}
	entity, ok := resource.(ResourceEntity)
	if !ok {
		return nil, fmt.Errorf("resource with id: [%d] is not ResourceEntity", identifier)
	}
	return entity, nil
}

// FoundResourceWithLevels find resources based on level
func (d *ResourceManagerUpGradationMultiLevel) FoundResourceWithLevels(resourceLevels []ResourceLevel) (result []ResourceEntity, err error) {
	defer tool.HandleErr(&err)
	result = make([]ResourceEntity, 0)
	if len(resourceLevels) <= 0 {
		return result, errors.New("resourceLevels is empty")
	}
	var computeLevelInstances map[interface{}]struct{}
	options := &ResourceOptions{
		PreOption:   make([]*interceptor.InterceptorOptions, 0),
		AfterOption: make([]*interceptor.InterceptorOptions, 0),
		DoExecute: func(container map[interface{}]interface{}) (err error) {
			for identifier := range computeLevelInstances {
				entity, ok := container[identifier]
				if !ok {
					return fmt.Errorf("resource with identifier [%v] not found", identifier)
				}
				resourceEntity, ok := entity.(ResourceEntity)
				if !ok {
					continue
				}
				result = append(result, resourceEntity)
			}
			if len(result) <= 1 {
				return
			}
			// 找到后进行排序
			sort.Slice(result, func(i, j int) bool {
				return d.Sort()(result[i], result[j])
			})
			return
		},
		Interrupt: false,
	}
	// the principle of a least level lookup
	if preInterceptorOptions, err := interceptor.GenerateInterceptorOptions(func() (err error) {
		min := math.MaxInt
		for _, level := range resourceLevels {
			levelInstances, ok := d.byLevel[level]
			if !ok {
				options.Interrupt = true
				return
			}
			if min > len(levelInstances) {
				min = len(levelInstances)
				computeLevelInstances = levelInstances
			}
		}
		if len(computeLevelInstances) <= 0 {
			return fmt.Errorf("not found resource with levels: [%v]", resourceLevels)
		}
		return
	}); err != nil {
		return result, err
	} else {
		options.PreOption = append(options.PreOption, preInterceptorOptions)
	}
	return result, d.resourceManager.OperateResource(options)
}

// FoundResourceWithCustomer customize resource finding
func (d *ResourceManagerUpGradationMultiLevel) FoundResourceWithCustomer(optionFun func(ResourceEntity) bool) (result []ResourceEntity, err error) {
	defer tool.HandleErr(&err)
	result = make([]ResourceEntity, 0)
	if optionFun == nil {
		return result, errors.New("identifier is empty")
	}
	options := &ResourceOptions{
		DoExecute: func(container map[interface{}]interface{}) (err error) {
			for _, resource := range container {
				entity, ok := resource.(ResourceEntity)
				if !ok {
					continue
				}
				// Customize how you find it
				if optionFun(entity) {
					result = append(result, entity)
				}
			}
			if len(result) <= 1 {
				return
			}
			sort.Slice(result, func(i, j int) bool {
				return d.Sort()(result[i], result[j])
			})
			return
		},
	}
	return result, d.resourceManager.OperateResource(options)
}

// Sort Sorting, the lower the priority, the higher the priority
func (d *ResourceManagerUpGradationMultiLevel) Sort() func(pre ResourceEntity, suf ResourceEntity) bool {
	return func(pre ResourceEntity, suf ResourceEntity) bool {
		return pre.Priority() < suf.Priority()
	}
}
